bitkeeper revision 1.1159.1.191 (41597997cc5ZJzvh6XLLSIhJ9hLEnA)
authormwilli2@equilibrium.research <mwilli2@equilibrium.research>
Tue, 28 Sep 2004 14:47:51 +0000 (14:47 +0000)
committermwilli2@equilibrium.research <mwilli2@equilibrium.research>
Tue, 28 Sep 2004 14:47:51 +0000 (14:47 +0000)
Initial support for automatic management of non-phy block devices.

.rootkeys
tools/examples/block-enbd [new file with mode: 0644]
tools/examples/block-file [new file with mode: 0644]
tools/examples/xend-config.sxp
tools/python/xen/xend/Blkctl.py [new file with mode: 0644]
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xend/XendRoot.py
tools/python/xen/xend/server/blkif.py

index 6ecc463f9b4eaf4f1199d50a83041d80738843ca..804e8a419d571b963c2c8b1b710a9d07603dcf7c 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 4124b308O8yPHMKbj4YPR_grPGZmdA tools/check/chk
 401d7e160vaxMBAUSLSicuZ7AQjJ3w tools/examples/Makefile
 401d7e16UgeqroJQTIhwkrDVkoWgZQ tools/examples/README
+41597996VhTbNuHbuscYSfRb-WR6fA tools/examples/block-enbd
+41597996GHP2_yVih2UspXh328fgMQ tools/examples/block-file
 405ff55dawQyCHFEnJ067ChPRoXBBA tools/examples/init.d/xend
 40278d94cIUWl2eRgnwZtr4hTyWT1Q tools/examples/init.d/xendomains
 40ee75a9xFz6S05sDKu-JCLqyVTkDA tools/examples/network
 40c9c468IienauFHQ_xJIcqnPJ8giQ tools/python/xen/util/ip.py
 4059c6a0pnxhG8hwSOivXybbGOwuXw tools/python/xen/util/tempfile.py
 40c9c468SNuObE_YWARyS0hzTPSzKg tools/python/xen/xend/Args.py
+41597996WNvJA-DVCBmc0xU9w_XmoA tools/python/xen/xend/Blkctl.py
 40c9c468Um_qc66OQeLEceIz1pgD5g tools/python/xen/xend/EventServer.py
 40c9c468U8EVl0d3G--8YXVg6VJD3g tools/python/xen/xend/EventTypes.py
 40c9c468QJTEuk9g4qHxGpmIi70PEQ tools/python/xen/xend/PrettyPrint.py
diff --git a/tools/examples/block-enbd b/tools/examples/block-enbd
new file mode 100644 (file)
index 0000000..cfae628
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Usage: block-enbd [bind server ctl_port |unbind node]
+#
+# The file argument to the bind command is the file we are to bind to a
+# loop device.  We print the path to the loop device node to stdout.
+#
+# The node argument to unbind is the name of the device node we are to
+# unbind.
+#
+# This assumes you're running a correctly configured server at the other end!
+
+case $1 in
+       bind)
+               for dev in /dev/nd*; do
+                       if nbd-client $2:$3 $dev; then
+                               echo $dev
+                               exit 0
+                       fi
+               done
+               exit 1
+       ;;
+
+       unbind)
+               nbd-client -d $2
+               exit 0
+       ;;
+
+       *)
+               echo 'Unknown command: ' $1
+               echo 'Valid commands are: bind, unbind'
+               exit 1
+esac
diff --git a/tools/examples/block-file b/tools/examples/block-file
new file mode 100644 (file)
index 0000000..362b1fa
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Usage: block_loop [bind file|unbind node]
+#
+# The file argument to the bind command is the file we are to bind to a
+# loop device.  We print the path to the loop device node to stdout.
+#
+# The node argument to unbind is the name of the device node we are to
+# unbind.
+
+case $1 in
+       bind)
+               for dev in /dev/loop*; do
+                       if losetup $dev $2; then
+                               echo $dev
+                               exit 0
+                       fi
+               done
+               exit 1
+       ;;
+
+       unbind)
+               losetup -d $2
+               exit 0
+       ;;
+
+       *)
+               echo 'Unknown command: ' $1
+               echo 'Valid commands are: bind, unbind'
+               exit 1
+esac
index f710a545298e9f493a4a326a713cde6a2747d648..ee5dbbc0a355448bc70506e57894b967780f2cb0 100644 (file)
@@ -21,3 +21,9 @@
 # virtual interfaces. Specify 'yes' or 'no'.
 (vif-antispoof     no)
 
+# Setup script for file-backed block devices
+(block-file block-file)
+
+# Setup script for enbd-backed block devices
+(block-enbd block-enbd)
+
diff --git a/tools/python/xen/xend/Blkctl.py b/tools/python/xen/xend/Blkctl.py
new file mode 100644 (file)
index 0000000..a5bda19
--- /dev/null
@@ -0,0 +1,42 @@
+"""Xend interface to block control scripts.
+"""
+import os
+import os.path
+import sys
+import string
+
+from xen.xend import XendRoot
+xroot = XendRoot.instance()
+
+"""Where network control scripts live."""
+SCRIPT_DIR = xroot.block_script_dir
+
+def block(op, type, dets, script=None):
+    """Call a block control script.
+    Xend calls this with op 'bind' when it is about to export a block device
+    (other than a raw partition).  The script is called with unbind when a
+    device is no longer in use and should be removed.
+
+    @param op:        operation (start, stop, status)
+    @param type:      type of block device (determines the script used)
+    @param dets:      arguments to the control script
+    @param script:    block script name
+    """
+    
+    if op not in ['bind', 'unbind']:
+        raise ValueError('Invalid operation:' + op)
+
+    # Special case phy devices - they don't require any (un)binding
+    if type == 'phy':
+        return dets
+    
+    if script is None:
+        script = xroot.get_block_script(type)
+    script = os.path.join(SCRIPT_DIR, script)
+    args = [op] + string.split(dets, ':')
+    args = ' '.join(args)
+    out = os.popen(script + ' ' + args)
+
+    output = out.readline()
+    out.close()
+    return string.rstrip(output)
index 8a8af83c10ebe13430d9da6b1d5370113f3a10b5..7f789a48f7e945ceb7be672c5aef85f799b270fc 100644 (file)
@@ -33,6 +33,8 @@ xend = server.SrvDaemon.instance()
 
 from XendError import VmError
 
+from server.blkif import blkdev_name_to_number
+
 """The length of domain names that Xen can handle.
 The names stored in Xen itself are not used for much, and
 xend can handle domain names of any length.
@@ -90,61 +92,6 @@ def shutdown_reason(code):
     """
     return shutdown_reasons.get(code, "?")
 
-def blkdev_name_to_number(name):
-    """Take the given textual block-device name (e.g., '/dev/sda1',
-    'hda') and return the device number used by the OS. """
-
-    if not re.match( '^/dev/', name ):
-       n = '/dev/' + name
-    else:
-       n = name
-        
-    try:
-       return os.stat(n).st_rdev
-    except:
-       pass
-
-    # see if this is a hex device number
-    if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
-       return string.atoi(name,16)
-       
-    return None
-
-def lookup_raw_partn(name):
-    """Take the given block-device name (e.g., '/dev/sda1', 'hda')
-    and return a dictionary { device, start_sector,
-    nr_sectors, type }
-        device:       Device number of the given partition
-        start_sector: Index of first sector of the partition
-        nr_sectors:   Number of sectors comprising this partition
-        type:         'Disk' or identifying name for partition type
-    """
-
-    n = blkdev_name_to_number(name)
-    if n:
-       return [ { 'device' : n,
-                  'start_sector' : long(0),
-                  'nr_sectors' : long(1L<<63),
-                  'type' : 'Disk' } ]
-    else:
-       return None
-
-def lookup_disk_uname(uname):
-    """Lookup a list of segments for a physical device.
-    
-    @param uname: name of the device in the format \'phy:dev\' for a physical device
-    @type  uname: string
-    @return: list of extents that make up the named device
-    @rtype: [dict]
-    """
-    ( type, d_name ) = string.split( uname, ':' )
-
-    if type == "phy":
-        segments = lookup_raw_partn( d_name )
-    else:
-        segments = None
-    return segments
-
 def make_disk(vm, config, uname, dev, mode, recreate=0):
     """Create a virtual disk device for a domain.
 
@@ -156,18 +103,12 @@ def make_disk(vm, config, uname, dev, mode, recreate=0):
     @return: deferred
     """
     idx = vm.next_device_index('vbd')
-    segments = lookup_disk_uname(uname)
-    if not segments:
-        raise VmError("vbd: Segments not found: uname=%s" % uname)
-    if len(segments) > 1:
-        raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
-    segment = segments[0]
     # todo: The 'dev' should be looked up in the context of the domain.
     vdev = blkdev_name_to_number(dev)
     if not vdev:
         raise VmError("vbd: Device not found: uname=%s dev=%s" % (uname, dev))
     ctrl = xend.blkif_create(vm.dom, recreate=recreate)
-    return ctrl.attachDevice(idx, config, vdev, mode, segment, recreate=recreate)
+    return ctrl.attachDevice(idx, config, uname, vdev, mode, recreate=recreate)
         
 def vif_up(iplist):
     """send an unsolicited ARP reply for all non link-local IP addresses.
index d9c6c800125fa19bc8c233ce4b4731eaffe7d2fd..8f188b5d69a4382d51681b1516aa182bafb4f9dc 100644 (file)
@@ -46,6 +46,9 @@ class XendRoot:
     """Where network control scripts live."""
     network_script_dir = "/etc/xen/scripts"
 
+    """Where block control scripts live."""
+    block_script_dir = "/etc/xen/scripts"
+
     logfile_default = "/var/log/xend.log"
 
     loglevel_default = 'DEBUG'
@@ -260,6 +263,9 @@ class XendRoot:
     def get_xend_address(self):
         return self.get_config_value('xend-address', '')
 
+    def get_block_script(self, type):
+        return self.get_config_value('block-%s' % type, '')
+
     def get_network_script(self):
         return self.get_config_value('network-script', 'network')
 
index 2cd5f1b2b3b780ddf8d68ef9385e335a8495cd39..69f9431f4884a74cad392f3d1913ea5293015346 100755 (executable)
@@ -5,9 +5,13 @@
 from twisted.internet import defer
 
 from xen.xend import sxp
+from xen.xend import Blkctl
 from xen.xend.XendLogging import log
-from xen.xend.XendError import XendError
+from xen.xend.XendError import XendError, VmError
 
+import os
+import re
+import string
 import channel
 import controller
 from messages import *
@@ -257,6 +261,11 @@ class BlkDev(controller.SplitDev):
             val.append(['uname', self.uname])
         return val
 
+    def unbind(self):
+        log.debug("Unbinding block dev (type %s) from %s"
+                  % (self.type, self.node))
+        Blkctl.block('unbind', self.type, self.node)
+
     def destroy(self, change=0):
         """Destroy the device. If 'change' is true notify the front-end interface.
 
@@ -266,6 +275,7 @@ class BlkDev(controller.SplitDev):
         d = self.send_be_vbd_destroy()
         if change:
             d.addCallback(lambda val: self.interfaceChanged())
+        d.addCallback(lambda val: self.unbind())
 
     def interfaceChanged(self):
         """Tell the back-end to notify the front-end that a device has been
@@ -345,7 +355,47 @@ class BlkDev(controller.SplitDev):
         backend.writeRequest(msg, response=d)
         return d
         
-        
+
+def blkdev_name_to_number(name):
+    """Take the given textual block-device name (e.g., '/dev/sda1',
+    'hda') and return the device number used by the OS. """
+
+    if not re.match( '^/dev/', name ):
+       n = '/dev/' + name
+    else:
+       n = name
+    
+    try:
+       return os.stat(n).st_rdev
+    except Exception, e:
+        print "blkdev_name_to_number> exception looking up device number for %s: %s" % (name, e)
+       pass
+
+    # see if this is a hex device number
+    if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
+       return string.atoi(name,16)
+       
+    return None
+
+def lookup_raw_partn(name):
+    """Take the given block-device name (e.g., '/dev/sda1', 'hda')
+    and return a dictionary { device, start_sector,
+    nr_sectors, type }
+        device:       Device number of the given partition
+        start_sector: Index of first sector of the partition
+        nr_sectors:   Number of sectors comprising this partition
+        type:         'Disk' or identifying name for partition type
+    """
+
+    n = blkdev_name_to_number(name)
+    if n:
+       return [ { 'device' : n,
+                  'start_sector' : long(0),
+                  'nr_sectors' : long(1L<<63),
+                  'type' : 'Disk' } ]
+    else:
+       return None
+
 class BlkifController(controller.SplitController):
     """Block device interface controller. Handles all block devices
     for a domain.
@@ -386,7 +436,7 @@ class BlkifController(controller.SplitController):
         self.devices[idx] = dev
         return dev
 
-    def attachDevice(self, idx, config, vdev, mode, segment, recreate=0):
+    def attachDevice(self, idx, config, uname, vdev, mode, recreate=0):
         """Attach a device to the specified interface.
         On success the returned deferred will be called with the device.
 
@@ -403,10 +453,30 @@ class BlkifController(controller.SplitController):
         @return: deferred
         @rtype:  Deferred
         """
+        if not recreate:
+            # Split into type and type-specific details (which are passed to the
+            # type-specific control script).
+            type, dets = string.split(uname, ':', 1)
+            # Special case: don't bother calling a script for phy.  Could
+            # alternatively provide a "do nothing" script for phy devices...
+            node = Blkctl.block('bind', type, dets)
+
+        segments = lookup_raw_partn(node)
+
+        if not segments:
+            raise VmError("vbd: Segments not found: uname=%s" % uname)
+        if len(segments) > 1:
+            raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
+
+        segment = segments[0]            
+
         dev = self.addDevice(idx, config, vdev, mode, segment)
+            
         if recreate:
             d = defer.succeed(dev)
         else:
+            dev.node = node
+            dev.type = type
             d = dev.attach()
         return d